#pragma once

#include <iostream>
#include <string>
#include <unordered_map>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <signal.h>
#include <unistd.h>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <pthread.h>
#include <ctype.h>

#include "ThreadPool/Log.hpp"
#include "ThreadPool/ThreadPool.hpp"
#include "ThreadPool/Task.hpp"

static void service(int sock, const std::string &clientip, const uint16_t &clientport, const std::string &thread_name)
{
    char buffer[1024];
    while(true)
    {
        ssize_t s = read(sock, buffer, sizeof(buffer)-1);
        if(s>0)
        {
            buffer[s] = 0;
            std::cout << thread_name << " | " << clientip << ":" << clientport << "# " << buffer << std::endl;
        }
        else if(s==0)
        {
            logMessage(NORMAL, "%s:%d Shutdown, Me Too!", clientip.c_str(), clientport);
            break;
        }
        else
        {
            logMessage(ERROR, "Read Socket Error, %d:%s", errno, strerror(errno));
            break;
        }
        write(sock, buffer, strlen(buffer));
    }
    close(sock);
}

class TcpServer
{
private:
    const static int gbacklog = 20;
public:
    TcpServer(uint16_t port, std::string ip="0.0.0.0")
        : _listensock(-1)
        , _port(port)
        , _ip(ip)
        , _threadpool_ptr(ThreadPool<Task>::getThreadPool())
    {}

    void initServer()
    {
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if(_listensock < 0)
        {
            logMessage(FATAL, "%d:%s", errno, strerror(errno));
            exit(2);
        }
        logMessage(NORMAL, "Create Socket Success, _listensock: %d", _listensock);

        struct sockaddr_in local;
        memset(&local, 0, sizeof local);
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        inet_pton(AF_INET, _ip.c_str(), &(local.sin_addr));
        if(bind(_listensock, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "Bind Error, %d:%s", errno, strerror(errno));
            exit(3);
        }

        if(listen(_listensock, gbacklog) < 0)
        {
            logMessage(FATAL, "Listen Error, %d:%s", errno, strerror(errno));
            exit(4);
        }
        logMessage(NORMAL, "Init Server Success!");
    }

    void start()
    {
        // signal(SIGCHLD, SIG_IGN);
        _threadpool_ptr->run();
        while(true)
        {
            // sleep(1);
            struct sockaddr_in src;
            socklen_t len = sizeof(src);
            int servicesock = accept(_listensock, (struct sockaddr*)&src, &len);
            if(servicesock < 0)
            {
                logMessage(ERROR, "Accept Error, %d:%s", errno, strerror(errno));
                continue;
            }
            uint16_t client_port = ntohs(src.sin_port);
            std::string client_ip = inet_ntoa(src.sin_addr);
            logMessage(NORMAL, "Link Success, Servicesock: %d | %s:%d |\n", servicesock, client_ip.c_str(), client_port);

            Task t(servicesock, client_ip, client_port, service);
            _threadpool_ptr->pushTask(t);
        }
    }

    ~TcpServer()
    {}
private:
    uint16_t _port;
    std::string _ip;
    int _listensock;
    std::unique_ptr<ThreadPool<Task>> _threadpool_ptr;
};